msg_tool\scripts\kirikiri\archive\xp3\crypt/
cz.rs1use super::*;
2use aes::Aes128Dec;
3use aes::cipher::{BlockModeDecrypt, KeyIvInit};
4use cbc::Decryptor;
5
6type Aes128CbcDec = Decryptor<Aes128Dec>;
7
8const CZ_MAGIC: &[u8; 4] = b"\xFD\xD7\x90\xA5";
9const CZ_IV_SEED: u32 = 0xBFBFBFBF;
10const CZ_HEADER_KEY: &[u8; 4] = b"\x9D\x1D\x9A\xF2";
11const CZ_DEFAULT_KEY: &[u8; 16] = b"\x91\x10\xfcuE\x8f\xb5\xe6\xfe\xac\xbaDvX\xc2\x1a";
12
13fn cz_decrypt_int(data: &[u8], offset: usize, key: u8) -> u32 {
14 let mut v: u32 = (data[offset] ^ key ^ CZ_HEADER_KEY[0]) as u32;
15 v |= ((data[offset + 1] ^ key ^ CZ_HEADER_KEY[1]) as u32) << 8;
16 v |= ((data[offset + 2] ^ key ^ CZ_HEADER_KEY[2]) as u32) << 16;
17 v |= ((data[offset + 3] ^ key ^ CZ_HEADER_KEY[3]) as u32) << 24;
18 v
19}
20
21fn cz_create_iv(seed: u32) -> [u8; 16] {
22 let mut state = [0u32; 4];
23 state[0] = 123456789;
24 state[1] = 972436830;
25 state[2] = 524018621;
26 state[3] = seed;
27 let mut iv = [0u8; 16];
28 for i in 0..16 {
29 let a = state[3];
30 let b = state[0] ^ (state[0] << 11);
31 state[0] = state[1];
32 state[1] = state[2];
33 state[2] = a;
34 state[3] = b ^ a ^ ((b ^ (a >> 11)) >> 8);
35 iv[i] = state[3] as u8;
36 }
37 iv
38}
39
40#[derive(Debug)]
41struct AesDecryptor<'a> {
42 aes: Aes128CbcDec,
43 entry: StreamRegion<Entry<'a>>,
44 pos: u64,
45 original_size: u64,
46}
47
48impl<'a> AesDecryptor<'a> {
49 fn new(
50 aes: Aes128CbcDec,
51 entry: StreamRegion<Entry<'a>>,
52 original_size: u64,
53 ) -> AlignedReader<16, Self> {
54 AlignedReader::new(Self {
55 aes,
56 entry,
57 pos: 0,
58 original_size,
59 })
60 }
61}
62
63impl<'a> Read for AesDecryptor<'a> {
64 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
65 let readed = self.entry.read_most(buf)?;
66 if readed % 16 != 0 {
67 return Err(std::io::Error::new(
68 std::io::ErrorKind::UnexpectedEof,
69 "Not enough data to decrypt",
70 ));
71 }
72 for i in (0..readed).step_by(16) {
74 let block: &mut [u8; 16] = (&mut buf[i..i + 16]).try_into().unwrap();
75 self.aes.decrypt_block(block.into());
76 }
77 let remaining = self.original_size - self.pos;
78 let readed = readed.min(remaining as usize);
79 self.pos += readed as u64;
80 Ok(readed)
81 }
82}
83
84#[derive(Debug)]
85pub struct KissCrypt {
86 base: BaseSchema,
87}
88
89impl KissCrypt {
90 pub fn new(base: BaseSchema) -> Self {
91 Self { base }
92 }
93}
94
95impl Crypt for KissCrypt {
96 fn hash_after_crypt(&self) -> bool {
97 self.base.hash_after_crypt
98 }
99 fn startup_tjs_not_encrypted(&self) -> bool {
100 self.base.startup_tjs_not_encrypted
101 }
102 fn obfuscated_index(&self) -> bool {
103 self.base.obfuscated_index
104 }
105 fn need_filter(&self, _filename: &str, buf: &[u8], buf_len: usize) -> bool {
106 buf_len >= 4 && buf.starts_with(CZ_MAGIC)
107 }
108 fn filter<'a>(&self, mut entry: Entry<'a>) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
109 let mut header = [0u8; 15];
110 entry.read_exact(&mut header)?;
111 let typ = [header[4] ^ 0x11, header[5] ^ 0x7F, header[6] ^ 0x9A];
112 let key = typ[0];
113 let _unpacked_size = cz_decrypt_int(&header, 7, key);
114 let packed_size = cz_decrypt_int(&header, 11, key);
115 if (packed_size as u64) < entry.index.original_size && (packed_size - 5) & 0xF == 0 {
116 let padded_size = packed_size - 5;
117 let original_size = padded_size
118 - (entry.peek_u8_at(15 + padded_size as u64 + 1)?
119 ^ entry.peek_u8_at(15 + padded_size as u64)?) as u32;
120 let iv_seed = entry.peek_u32_at(15 + padded_size as u64 + 1)? ^ CZ_IV_SEED;
121 let aes = Aes128CbcDec::new(CZ_DEFAULT_KEY.into(), &cz_create_iv(iv_seed).into());
122 let entry = StreamRegion::with_size(entry, padded_size as u64)?;
123 let stream = AesDecryptor::new(aes, entry, original_size as u64);
124 if typ[0] == b'C' {
125 let stream = flate2::read::ZlibDecoder::new(stream);
126 return Ok(Box::new(stream));
127 }
128 Ok(Box::new(stream))
129 } else {
130 Ok(Box::new(entry))
131 }
132 }
133 fn decrypt_supported(&self) -> bool {
134 true
135 }
136 fn decrypt_seek_supported(&self) -> bool {
137 true
138 }
139 fn decrypt<'a>(
140 &self,
141 entry: &Xp3Entry,
142 cur_seg: &Segment,
143 stream: Box<dyn Read + Send + Sync + 'a>,
144 ) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
145 let key = entry.file_hash ^ (entry.file_hash >> 19) ^ 0x4A9EEFF0;
146 Ok(Box::new(KissCryptReader::new(stream, cur_seg, key)))
147 }
148 fn decrypt_with_seek<'a>(
149 &self,
150 entry: &Xp3Entry,
151 cur_seg: &Segment,
152 stream: Box<dyn ReadSeek + Send + Sync + 'a>,
153 ) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
154 let key = entry.file_hash ^ (entry.file_hash >> 19) ^ 0x4A9EEFF0;
155 Ok(Box::new(KissCryptReader::new(stream, cur_seg, key)))
156 }
157}
158
159impl<R: Read> Read for KissCryptReader<R> {
160 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
161 let readed = self.inner.read(buf)?;
162 let offset = self.seg_start + self.pos;
163 let mut i = 0usize;
164 while (i as u64 + offset) & 0xF != 0 {
165 i += 1;
166 }
167 while i < readed {
168 buf[i] ^= (self.key ^ (offset as u32 + i as u32)) as u8;
169 i += 0x10;
170 }
171 self.pos += readed as u64;
172 Ok(readed)
173 }
174}